﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.NumericalIntegration
{
    /// <summary>
    /// This class provides bivariate numerical integration by using a grid. EXPERIMANTEL CODE.
    /// </summary>
    [Serializable]
    internal class InternalBivariateGridIntegrator
    {
        /// <summary>
        /// This field holds the first dimension lower bound of the integral.
        /// </summary>
        private double a1;

        /// <summary>
        /// This field holds the second dimension lower bound of the integral.
        /// </summary>
        private double a2;

        /// <summary>
        /// This field holds the first dimension upper bound of the integral.
        /// </summary>
        private double b1;

        /// <summary>
        /// This field holds the second dimension upper bound of the integral.
        /// </summary>
        private double b2;

        /// <summary>
        /// This field holds the bivariate integral.
        /// </summary>
        private BivariateIntegral integral;

        /// <summary>
        /// Initializes a new instance of the <see cref="InternalBivariateGridIntegrator"/> class.
        /// </summary>
        /// <param name="integral">The bivariate integral to integrate.</param>
        public InternalBivariateGridIntegrator(BivariateIntegral integral)
        {
            this.integral = integral;
        }

        /// <summary>
        /// Gets or sets the bivariate integral to integrate.
        /// </summary>
        /// <value>The bivariate integral to integrate.</value>
        public BivariateIntegral Integral
        {
            get { return integral; }
            set { integral = value; }
        }

        public double Integrate(int girdSizeX, int girdSizeY)
        {
            double sign1 = 1;
            double sign2 = 1;
            this.a1 = this.integral.A1;
            this.a2 = this.integral.A2;
            this.b1 = this.integral.B1;
            this.b2 = this.integral.B2;

            if (b1 < a1)
            {
                double tempuri = b1;

                b1 = a1;
                a1 = tempuri;
                sign1 = -1;
            }

            if (b2 < a2)
            {
                double tempuri = b2;

                b2 = a2;
                a2 = tempuri;
                sign2 = -1;
            }

            double result = 0;
            BivariateFunctionSampler sampler = new BivariateFunctionSampler(this.integral.Function);

            //double[,] samplingResult = sampler.SampleBivariateFunction(new Point2D(this.a1, this.b1),
            //                                                   new Point2D(this.b2, this.a2), girdSizeX, girdSizeY);

            double[,] samplingResult = sampler.SampleBivariateFunction(new Point2D(this.a1, this.b1),
                                                                       new Point2D(this.a2, this.b2), girdSizeX,
                                                                       girdSizeY);

            double ground = sampler.SamplingIntervalX * sampler.SamplingIntervalY;

            for (int i = 1; i < samplingResult.GetLength(0); i++)
            {
                for (int j = 1; j < samplingResult.GetLength(1); j++)
                {
                    double a = samplingResult[i, j];
                    double b = samplingResult[i - 1, j];
                    double c = samplingResult[i, j - 1];
                    double d = samplingResult[i - 1, j - 1];

                    result += ground * Avg(a, b, c, d);
                }
            }

            if (sign1 == sign2)
            {
                return result;
            }

            return -result;
        }

        private static double Min(double a, double b, double c, double d)
        {
            double minA = Math.Min(a, b);
            double minB = Math.Min(c, d);

            return Math.Min(minA, minB);
        }

        private static double Avg(double a, double b, double c, double d)
        {
            return (a + b + c + d) / 4;
        }
    }
}